﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DataAssociations.AssociationMiningAlgorithms.Helpers;

namespace DataAssociations.AssociationMiningAlgorithms.Apriori
{
    public class Mining
    {
        public static ItemsetCollection DoApriori(ItemsetCollection db, double supportThreshold)
        {
            Itemset I = db.GetUniqueItems();
            ItemsetCollection L = new ItemsetCollection(); //resultant large itemsets
            ItemsetCollection Li = new ItemsetCollection(); //large itemset in each iteration
            ItemsetCollection Ci = new ItemsetCollection(); //pruned itemset in each iteration

            ////creates a list of unique items in a 1-itemset Candidate Itemset
            foreach (var item in I)
            {
                Ci.Add(new Itemset() { item });
            }

            //next iterations
            int k = 2;
            while (Ci.Count != 0)
            {
                //set Li from Ci (pruning)
                Li.Clear();
                foreach (Itemset itemset in Ci)
                {
                    itemset.Support = db.FindSupport(itemset);
                    if (itemset.Support >= supportThreshold)
                    {
                        Li.Add(itemset);
                        L.Add(itemset);
                    }
                }

                //set Ci for next iteration (find supersets of Li)
                Ci.Clear();
                Ci.AddRange(Combinatorics.FindSubsets(Li.GetUniqueItems(), k)); //get k-item subsets
                k += 1;
            }

            return (L);
        }

        public static List<AssociationRule> Mine(ItemsetCollection db, ItemsetCollection L, double confidenceThreshold)
        {
            List<AssociationRule> allRules = new List<AssociationRule>();

            foreach (Itemset itemset in L)
            {
                ItemsetCollection subsets = Combinatorics.FindSubsets(itemset, 0); //get all subsets
                foreach (Itemset subset in subsets)
                {
                    double confidence = (db.FindSupport(itemset) / db.FindSupport(subset)) * 100.0;
                    if (confidence >= confidenceThreshold)
                    {
                        AssociationRule rule = new AssociationRule();
                        rule.X.AddRange(subset);
                        rule.Y.AddRange(itemset.Remove(subset));
                        rule.Support = db.FindSupport(itemset);
                        rule.Confidence = confidence;
                        if (rule.X.Count > 0 && rule.Y.Count > 0)
                        {
                            allRules.Add(rule);
                        }
                    }
                }
            }

            return (allRules);
        }

        public static void GetResults(StringBuilder sb, List<List<string>> inputSets, int minSupport, int minConfidence)
        {
            var _db = new ItemsetCollection();
            foreach (var inputSet in inputSets)
            {
                var itemSet = new Itemset();
                try
                {
                    itemSet.AddRange(inputSet.Select(x => Convert.ToInt64(x)).ToList());
                }
                catch { }
                _db.Add(itemSet);
            }
            Itemset uniqueItems = _db.GetUniqueItems();
            sb.AppendLine("Items: " + uniqueItems);
            sb.AppendLine(string.Empty);

            //do apriori
            ItemsetCollection L = Mining.DoApriori(_db, minSupport);
            sb.AppendLine(L.Count + " Large Itemsets after Apriori analisys");
            foreach (Itemset itemset in L)
            {
                sb.AppendLine(itemset.ToString());
            }
            sb.AppendLine(string.Empty);

            //do mining
            List<AssociationRule> allRules = Mining.Mine(_db, L, minConfidence);
            sb.AppendLine(allRules.Count + " Association Rules");
            foreach (AssociationRule rule in allRules)
            {
                sb.AppendLine(rule.ToString());
            }
        }
    }
}

